package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"image"
	"image/color"
	"image/draw"
	_ "image/gif"
	_ "image/jpeg"
	"image/png"
	_ "image/png"
	"io"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"github.com/makiuchi-d/gozxing"
	"github.com/makiuchi-d/gozxing/multi/qrcode"
	qr "github.com/skip2/go-qrcode"
	"golang.org/x/image/font"
)

func externalIP() (net.IP, error) {
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			ip := getIpFromAddr(addr)
			if ip == nil {
				continue
			}
			return ip, nil
		}
	}
	return nil, errors.New("connected to the network?")
}

func getIpFromAddr(addr net.Addr) net.IP {
	var ip net.IP
	switch v := addr.(type) {
	case *net.IPNet:
		ip = v.IP
	case *net.IPAddr:
		ip = v.IP
	}
	if ip == nil || ip.IsLoopback() {
		return nil
	}
	ip = ip.To4()
	if ip == nil {
		return nil // not an ipv4 address
	}

	return ip
}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func upload(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	if r.Method == "GET" {
		t, err := template.ParseFiles("index.html")
		if err != nil {
			fmt.Fprintf(w, "parse template error: %s", err.Error())
			return
		}
		t.Execute(w, nil)
	} else {
		fmt.Fprint(w, "非法访问")
	}
}

const (
	Separator = os.PathSeparator // 路径分隔符（分隔路径元素）
)

//TextBrush 字体相关
type TextBrush struct {
	FontType  *truetype.Font
	FontSize  float64
	FontColor *image.Uniform
	TextWidth int
}

//NewTextBrush 新生成笔刷
func NewTextBrush(FontFilePath string, FontSize float64, FontColor *image.Uniform, textWidth int) (*TextBrush, error) {
	fontFile, err := ioutil.ReadFile(FontFilePath)
	if err != nil {
		return nil, err
	}
	fontType, err := truetype.Parse(fontFile)
	if err != nil {
		return nil, err
	}
	if textWidth <= 0 {
		textWidth = 20
	}
	return &TextBrush{FontType: fontType, FontSize: FontSize, FontColor: FontColor, TextWidth: textWidth}, nil
}

func genQrcode(w http.ResponseWriter, r *http.Request) {
	queryForm, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		panic(err)
	}
	log.Println(queryForm)
	r.ParseForm()
	r.ParseMultipartForm(128)
	qrcodeLens := r.Form["qrcodeLen"]
	contents := r.Form["content"]
	names := r.Form["name"]
	content := contents[0]
	name := names[0]
	var qrcodeLen int = 1000
	if len(qrcodeLens) > 0 {
		qrcodeLenTemp, _ := strconv.Atoi(qrcodeLens[0])
		if qrcodeLenTemp > 0 {
			qrcodeLen = qrcodeLenTemp
		}
	}
	contentLen := len(content)
	imageSize := 500
	padding := 20
	qrcodeCount := int(math.Ceil(float64(contentLen) / float64(qrcodeLen)))
	log.Printf("qrcodeCount: %d", qrcodeCount)
	width := (imageSize + padding*2)
	if qrcodeCount > 1 {
		width = width * 2
	}
	height := (imageSize + padding*2) * int(math.Ceil(float64(qrcodeCount)/float64(2)))
	line := 0
	des := image.NewRGBA(image.Rect(0, 0, width, height))
	for index := 0; index < qrcodeCount; index++ {
		end := (index + 1) * qrcodeLen
		qrcodeContent := ""
		if end <= contentLen {
			qrcodeContent = content[(index * qrcodeLen):end]
		} else {
			qrcodeContent = content[(index * qrcodeLen):contentLen]
		}
		code, _ := qr.New(fmt.Sprintf("%d", index)+"-"+qrcodeContent, qr.Medium)
		code.DisableBorder = true
		qrImage := code.Image(imageSize)
		x := padding
		if (index+1)%2 == 0 {
			x = padding + (imageSize + padding)
		}
		y := line*imageSize + (line+1)*padding
		log.Printf("%d x %d", x, y)
		draw.Draw(des, des.Bounds(), qrImage, qrImage.Bounds().Min.Sub(image.Pt(x, y)), draw.Src)
		if (index+1)%2 == 0 {
			line++
		}
	}
	//新建笔刷
	textBrush, _ := NewTextBrush("font/微软vista雅黑.ttf", 20, image.Black, 50)
	c := freetype.NewContext()
	c.SetDPI(72)
	c.SetFont(textBrush.FontType)
	c.SetHinting(font.HintingFull)
	c.SetFontSize(textBrush.FontSize)
	c.SetClip(des.Bounds())
	c.SetDst(des)
	textBrush.FontColor = image.NewUniform(color.RGBA{
		R: 0xFF,
		G: 0,
		B: 0,
		A: 255,
	})
	c.SetSrc(textBrush.FontColor)

	c.DrawString(name, freetype.Pt(10, 16))
	var b bytes.Buffer
	png.Encode(&b, des)
	contentQr := bytes.NewReader(b.Bytes())

	w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, "qrcode.png"))
	w.Header().Add("Content-Type", "image/png")
	http.ServeContent(w, r, "qrcode.png", time.Now(), contentQr)
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	uFile, uFileHeader, errFile := r.FormFile("file")
	// uFile, _, errFile := r.FormFile("file")
	fn := r.Form["fileName"]
	fileName := uFileHeader.Filename
	log.Println(fileName)
	suffix := path.Ext(fileName)
	log.Println(suffix)
	if errFile != nil {
		log.Fatalln("error1")
		panic(errFile)
	}
	defer uFile.Close()
	// id := uuid.NewV4()
	// ids := id.String()
	// rand.Seed(time.Now().Unix())
	// println(ids)
	// fileName := strconv.Itoa(rand.Intn(100000000)) + ids //+ ".jpg"
	// fileNameNew := strconv.Itoa(rand.Intn(100000000)) + "-" + ids + suffix //+ uFileHeader.Filename
	fileNameNew := ""
	if fn == nil {
		fileNameNew = fileName
		if strings.Contains(fileName, "/") {
			fileNameArr := strings.Split(fileName, "/")
			fileNameArrLen := len(fileNameArr)
			fileNameNew = fileNameArr[fileNameArrLen-1]
		} else if strings.Contains(fileName, "\\") {
			fileNameArr := strings.Split(fileName, "\\")
			fileNameArrLen := len(fileNameArr)
			fileNameNew = fileNameArr[fileNameArrLen-1]
		}
	} else {
		fileNameNew = fn[0]
	}
	currentPath, errCurrentPath := os.Getwd()
	if errCurrentPath != nil {
		log.Fatalln("error2")
		panic(errCurrentPath)
	}
	// filesDir := currentPath + string(Separator) + "files"
	filesDir := currentPath
	dirExist, errDirExist := PathExists(filesDir)
	if errDirExist != nil {
		log.Fatalln("error3")
		panic(errDirExist)
	}
	if !dirExist {
		errMkdirAll := os.Mkdir(filesDir, os.ModePerm)
		if errMkdirAll != nil {
			log.Fatalln("error4")
			panic(errMkdirAll)
		}
	}
	filePath := filesDir + string(Separator) + fileNameNew
	println(filePath)
	file, err := os.Create(filePath)
	if err != nil {
		log.Fatalln("error5")
		panic(err)
	}
	defer file.Close()
	// _, err = io.Copy(file, r.Body)
	_, err = io.Copy(file, uFile)
	if err != nil {
		log.Fatalln("error6")
		panic(err)
	}
	w.Write([]byte("upload success(" + fileNameNew + ")"))
}

func readQrCode(fileName string) []*gozxing.Result {
	fi, err := os.Open(fileName)
	if err != nil {
		log.Println(err)
		return []*gozxing.Result{}
	}
	defer fi.Close()
	img, _, err := image.Decode(fi)

	if err != nil {
		log.Println(err)
		return []*gozxing.Result{}
	}

	bmp, _ := gozxing.NewBinaryBitmapFromImage(img)
	qrReader := qrcode.NewQRCodeMultiReader()
	result, err := qrReader.DecodeMultiple(bmp, nil)
	if err != nil {
		log.Println(err)
	}

	log.Println(result)

	return result
}

func qrcodeHandler(w http.ResponseWriter, r *http.Request) {
	queryForm, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		panic(err)
	}
	log.Println(queryForm)
	r.ParseForm()
	r.ParseMultipartForm(128)
	// uFile, _, errFile := r.FormFile("file")
	fn := r.Form["img"]
	// fmt.Println(fn[0])
	base64Str := ""
	if strings.Index(fn[0], ",") != -1 {
		base64Str = fn[0][strings.Index(fn[0], ",")+1:]
	} else {
		base64Str = fn[0]
	}
	dist, _ := base64.StdEncoding.DecodeString(base64Str)
	//写入新文件
	f, _ := os.OpenFile("qrcode.jpg", os.O_RDWR|os.O_CREATE, os.ModePerm)
	defer f.Close()
	f.Write(dist)
	// content, err := gozbarlib.QRCodeReader("qrcode.jpg")
	// if err != nil {
	// 	panic(err)
	// }
	// contentArr := strings.Split(content, "\n")
	contentArr := readQrCode("qrcode.jpg")
	array := make([]string, len(contentArr))
	fmt.Println(len(contentArr))
	for i := 0; i < len(contentArr); i++ {
		contentTmp := contentArr[i].GetText()
		log.Println(i)
		log.Println(contentTmp)
		if len(contentTmp) == 0 {
			continue
		}
		contentTmpArr := strings.Split(contentTmp, "-")
		index, _ := strconv.Atoi(contentTmpArr[0])
		array[index] = contentTmpArr[1]
	}
	result := ""
	for i := 0; i < len(contentArr); i++ {
		result += array[i]
	}
	log.Println("content of image is :", contentArr)
	log.Println(result)
	resultMap := map[string]string{"content": result}
	resultJson, err := json.Marshal(resultMap)
	if err != nil {
		panic(err)
	}
	w.Write([]byte(resultJson))
}
func qrcodeFileHandler(w http.ResponseWriter, r *http.Request) {
	uFile, uFileHeader, errFile := r.FormFile("file")
	queryForm, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		log.Println(err)
		resultMap := map[string]string{"code": "0", "content": "", "msg": err.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	fmt.Println(queryForm)
	r.ParseForm()
	r.ParseMultipartForm(128)
	// uFile, _, errFile := r.FormFile("file")
	fn := r.Form["fileName"]
	log.Println(fn)
	fileName := uFileHeader.Filename
	log.Println(fileName)
	suffix := path.Ext(fileName)
	log.Println(suffix)
	if errFile != nil {
		log.Println("error1")
		log.Println(errFile)
		resultMap := map[string]string{"code": "0", "content": "", "msg": errFile.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	defer uFile.Close()
	// id := uuid.NewV4()
	// ids := id.String()
	rand.Seed(time.Now().Unix())
	// println(ids)
	// fileName := strconv.Itoa(rand.Intn(100000000)) + ids //+ ".jpg"
	// fileNameNew := strconv.Itoa(rand.Intn(100000000)) + "-" + ids + suffix //+ uFileHeader.Filename
	fileNameNew := ""
	if fn == nil {
		fileNameNew = fileName
		if strings.Contains(fileName, "/") {
			fileNameArr := strings.Split(fileName, "/")
			fileNameArrLen := len(fileNameArr)
			fileNameNew = fileNameArr[fileNameArrLen-1]
		} else if strings.Contains(fileName, "\\") {
			fileNameArr := strings.Split(fileName, "\\")
			fileNameArrLen := len(fileNameArr)
			fileNameNew = fileNameArr[fileNameArrLen-1]
		}
		fileNameNew = strconv.Itoa(rand.Intn(100000000)) + "-" + fileNameNew
	} else {
		for i := 0; i < len(fn); i++ {
			if !(strings.Trim(fn[i], "?") == ".png" || strings.Trim(fn[i], "?") == ".jpg" || strings.Trim(fn[i], "?") == "") {
				fileNameNew = fn[i]
				break
			}
		}
	}
	currentPath, errCurrentPath := os.Getwd()
	if errCurrentPath != nil {
		log.Println("error2")
		log.Println(errCurrentPath)
		resultMap := map[string]string{"code": "0", "content": "", "msg": errCurrentPath.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	filesDir := currentPath + string(Separator) + "images"
	// filesDir := currentPath
	dirExist, errDirExist := PathExists(filesDir)
	if errDirExist != nil {
		log.Println("error3")
		log.Println(errDirExist)
		resultMap := map[string]string{"code": "0", "content": "", "msg": errDirExist.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	if !dirExist {
		errMkdirAll := os.Mkdir(filesDir, os.ModePerm)
		if errMkdirAll != nil {
			log.Println("error4")
			log.Println(errMkdirAll)
			resultMap := map[string]string{"code": "0", "content": "", "msg": errMkdirAll.Error()}
			resultJson, _ := json.Marshal(resultMap)
			w.Write([]byte(resultJson))
			return
		}
	}
	filePath := filesDir + string(Separator) + fileNameNew
	log.Println(filePath)
	file, err := os.Create(filePath)
	if err != nil {
		log.Println("error5")
		log.Println(err)
		resultMap := map[string]string{"code": "0", "content": "", "msg": err.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	defer file.Close()
	// _, err = io.Copy(file, r.Body)
	_, err = io.Copy(file, uFile)
	if err != nil {
		log.Println("error6")
		log.Println(err)
		resultMap := map[string]string{"code": "0", "content": "", "msg": err.Error()}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	// content, err := gozbarlib.QRCodeReader(fileNameNew)
	// if err != nil {
	// 	panic(err)
	// }
	// contentArr := strings.Split(content, "\n")
	contentArr := readQrCode(filePath)
	if len(contentArr) == 0 {
		resultMap := map[string]string{"code": "0", "content": "", "msg": "没有识别到二维码"}
		resultJson, _ := json.Marshal(resultMap)
		w.Write([]byte(resultJson))
		return
	}
	array := make([]string, len(contentArr))
	indexOut := 0
	// fmt.Println(len(contentArr))
	for i := 0; i < len(contentArr); i++ {
		contentTmp := contentArr[i].GetText()
		log.Println(i)
		log.Println(contentTmp)
		if len(contentTmp) == 0 {
			continue
		}
		contentTmpArr := strings.Split(contentTmp, "-")
		index, err := strconv.Atoi(contentTmpArr[0])
		if err == nil && index < len(array) {
			if len(contentTmpArr) == 1 {
				array[index] = contentTmpArr[0]
			} else if len(contentTmpArr) > 1 {
				array[index] = contentTmpArr[1]
			} else {
				array[index] = ""
			}
		} else {
			array[indexOut] = contentTmp
		}
		indexOut += 1
	}
	result := ""
	for i := 0; i < len(contentArr); i++ {
		result += array[i]
	}
	log.Println("content of image is :", contentArr)
	log.Println(result)
	resultMap := map[string]string{"code": "1", "content": result, "msg": "success"}
	resultJson, err := json.Marshal(resultMap)
	if err != nil {
		panic(err)
	}
	w.Write([]byte(resultJson))
}

func cors(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")                                                            // 允许访问所有域，可以换成具体url，注意仅具体url才能带cookie信息
		w.Header().Add("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") //header的类型
		w.Header().Add("Access-Control-Allow-Credentials", "true")                                                    //设置为true，允许ajax异步请求带cookie信息
		w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")                             //允许请求方法
		w.Header().Set("content-type", "application/json;charset=UTF-8")                                              //返回数据格式是json
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		f(w, r)
	}
}

type LogInfo struct {
	TimeStamp string `json:"@timeStamp,string"`
	AppName   string `json:"appName,string"`
	Event     string `json:"event,string"`
	Msg       string `json:"msg,string"`
}

func logHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	r.ParseMultipartForm(128)
	appName := r.Form["appName"]
	timeStamp := r.Form["time"]
	event := r.Form["event"]
	msg := r.Form["msg"]
	var l LogInfo
	jsonObj := make(map[string]interface{})
	if timeStamp != nil {
		l.TimeStamp = timeStamp[0]
	} else {
		timeStr := time.Now().Format("2006-01-02T15:04:05.000Z")
		l.TimeStamp = timeStr
	}
	jsonObj["@timestamp"] = l.TimeStamp
	var logPath string
	if appName != nil {
		l.AppName = appName[0]
	} else {
		l.AppName = "V-APP"
	}
	logPath = "logs-" + l.AppName + "-default"
	if event != nil {
		l.Event = event[0]
	} else {
		l.Event = "V-EVENT"
	}
	if msg != nil {
		l.Msg = msg[0]
	} else {
		l.Msg = "[[empty]]"
	}
	jsonObj["event"] = l.Event
	jsonObj["appName"] = l.AppName
	jsonObj["msg"] = l.Msg

	jsonB, err := json.Marshal(jsonObj)

	b, err := json.Marshal(l)
	if err != nil {
		log.Println("JSON ERR:", err)
	}
	b = jsonB
	log.Println(string(b))
	url := "http://localhost:9200/" + logPath + "/_doc"
	log.Println(url)
	client := &http.Client{}
	reqest, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
	reqest.Header.Set("Content-Type", "application/json; charset=utf-8")
	if err != nil {
		panic(err)
	}

	//处理返回结果
	response, err := client.Do(reqest)
	if err != nil {
		log.Fatalln(err)
		return
	}

	//将结果定位到标准输出 也可以直接打印出来 或者定位到其他地方进行相应的处理
	// stdout := os.Stdout
	// _, err = io.Copy(stdout, response.Body)

	// //返回的状态码
	// status := response.StatusCode

	// fmt.Println(status)

	defer response.Body.Close() //ok, most of the time :-)
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatalln(err)
		return
	}

	log.Println(string(body))

	w.Write([]byte(body))
}

func Shellout(command string) (error, string, string) {
	log.Println(command)
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("/bin/bash", "-c", command)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	return err, stdout.String(), stderr.String()
}

func deleteHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	r.ParseMultipartForm(128)
	fileNames := r.Form["filename"]
	log.Println("删除文件", fileNames[0])
	cmd := exec.Command("rm", fileNames[0])
	p, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	downloadFilesDir := p + "/downloadFiles"
	cmd.Dir = downloadFilesDir
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Waiting for command to finish...")
	err = cmd.Wait()
	log.Printf("Command finished with error: %v", err)
	if err != nil { // 读取输出结果
		w.Write([]byte("error"))
	} else {
		w.Write([]byte("success"))
	}
}

func downloadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	r.ParseMultipartForm(128)
	urls := r.Form["url"]
	fileNames := r.Form["filename"]
	log.Println("下载", urls[0], fileNames[0])
	cmd := exec.Command("wget", "-c", urls[0], "-O", fileNames[0])

	p, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	downloadFilesDir := p + "/downloadFiles"
	os.MkdirAll(downloadFilesDir, os.ModePerm)
	cmd.Dir = downloadFilesDir
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Waiting for command to finish...")
	err = cmd.Wait()
	log.Printf("Command finished with error: %v", err)
	if err != nil { // 读取输出结果
		w.Write([]byte("error"))
	} else {
		w.Write([]byte("success"))
	}
}

func main() {
	port := "9208"
	log.Println("serve start")
	ip, err := externalIP()
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(ip.String() + ":" + port)
	http.HandleFunc("/upload", cors(uploadHandler))
	http.HandleFunc("/cibproxy/api/qrcode", cors(qrcodeHandler))
	http.HandleFunc("/cibproxy/api/qrcode.do", cors(qrcodeHandler))
	http.HandleFunc("/cib/api/qrcode", cors(qrcodeHandler))
	http.HandleFunc("/cib/api/qrcode.do", cors(qrcodeHandler))
	http.HandleFunc("/cibproxy/api/qrcodeFile", cors(qrcodeFileHandler))
	http.HandleFunc("/cibproxy/api/qrcodeFile.do", cors(qrcodeFileHandler))
	http.HandleFunc("/cib/api/qrcodeFile", cors(qrcodeFileHandler))
	http.HandleFunc("/cib/api/qrcodeFile.do", cors(qrcodeFileHandler))
	http.HandleFunc("/cibproxy/api/qrcodeGen", cors(genQrcode))
	http.HandleFunc("/cibproxy/api/qrcodeGen.do", cors(genQrcode))
	http.HandleFunc("/upload.html", upload)
	http.HandleFunc("/log", logHandler)
	http.HandleFunc("/download", downloadHandler)
	http.HandleFunc("/delete", deleteHandler)
	p, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	log.Println(p)
	http.Handle("/", http.FileServer(http.Dir(p+"/downloadFiles")))
	// 设置静态目录
	fsh := http.FileServer(http.Dir(p + "/web"))
	http.Handle("/web/", http.StripPrefix("/web/", fsh))
	http.ListenAndServe(":"+port, nil)
}
